home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 21 / AACD 21.iso / AACD / Utilities / Ghostscript / src / gdevifno.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-01-01  |  16.7 KB  |  790 lines

  1. /*
  2.  *        Copyright (c) 1998 by Lucent Technologies.
  3.  * Permission to use, copy, modify, and distribute this software for any
  4.  * purpose without fee is hereby granted, provided that this entire notice
  5.  * is included in all copies of any software which is or includes a copy
  6.  * or modification of this software.
  7.  *
  8.  * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
  9.  * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
  10.  * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
  11.  * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
  12.  */
  13.  
  14. /* $Id: gdevifno.c,v 1.1 2000/03/09 08:40:41 lpd Exp $ */
  15. /*
  16.  * gs device to generate inferno bitmaps
  17.  *
  18.  * Russ Cox <rsc@plan9.bell-labs.com>, 3/25/98
  19.  * Updated to fit in the standard GS distribution, 5/14/98
  20.  * Comments edited for automatic TOC generation, 11/4/99
  21.  */
  22.  
  23. #include "gdevprn.h"
  24. #include "gsparam.h"
  25. #include "gxlum.h"
  26. #include <stdlib.h>
  27.  
  28. #define nil ((void*)0)
  29.  
  30. /*
  31.  * ERROR 
  32.  * is used to go up the stack and 
  33.  * eventually return_error(gs_error_Fatal) to gs.
  34.  */
  35. #define ERROR (-2)
  36.  
  37. typedef struct WImage WImage;
  38. typedef struct Rectangle Rectangle;
  39. typedef struct Point Point;
  40.  
  41. struct Point {
  42.     int x;
  43.     int y;
  44. };
  45.  
  46. struct Rectangle {
  47.     Point min;
  48.     Point max;
  49. };
  50. private Point ZP = { 0, 0 };
  51.  
  52. private WImage* initwriteimage(FILE *f, Rectangle r, int ldepth);
  53. private int writeimageblock(WImage *w, uchar *data, int ndata);
  54. private int bytesperline(Rectangle, int);
  55. private int rgb2cmap(int, int, int);
  56. private long cmap2rgb(int);
  57.  
  58. #define X_DPI    100
  59. #define Y_DPI    100
  60.  
  61. private dev_proc_map_rgb_color(inferno_rgb2cmap);
  62. private dev_proc_map_color_rgb(inferno_cmap2rgb);
  63. private dev_proc_open_device(inferno_open);
  64. private dev_proc_close_device(inferno_close);
  65. private dev_proc_print_page(inferno_print_page);
  66.  
  67. typedef struct inferno_device_s {
  68.     gx_device_common;
  69.     gx_prn_device_common;
  70.     int ldepth;
  71.     int lastldepth;
  72.     int color, gray;
  73.     int cmapcall;
  74.     int nbits;
  75. } inferno_device;
  76.  
  77. private const gx_device_procs inferno_procs =
  78.     prn_color_params_procs(inferno_open, gdev_prn_output_page, inferno_close,
  79.         inferno_rgb2cmap, inferno_cmap2rgb,
  80.         gdev_prn_get_params, gdev_prn_put_params);
  81.  
  82.  
  83. inferno_device far_data gs_inferno_device =
  84. { prn_device_body(gx_device_printer, inferno_procs, "inferno",
  85.     DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
  86.     X_DPI, Y_DPI,
  87.     0,0,0,0,    /* margins */
  88.     3,        /* 3 = RGB, 1 = gray, 4 = CMYK */
  89.     16,        /* # of bits per pixel -- 16 is easier to handle than 12 */
  90.     255,        /* # of distinct gray levels.  >=31 to fool gs into asking us*/
  91.     255,        /* # of distinct color levels.  >= 31 to fool gs */
  92.     0,        /* dither gray ramp size.  hopefully not used */
  93.     0,            /* dither color ramp size.  hopefully not used */
  94.     inferno_print_page)
  95. };
  96.  
  97. /*
  98.  * ghostscript asks us how to convert between
  99.  * rgb and color map entries
  100.  */
  101. private gx_color_index 
  102. inferno_rgb2cmap(P4(gx_device *dev, gx_color_value red,
  103.   gx_color_value green, gx_color_value blue)) {
  104.     int shift;
  105.     inferno_device *bdev = (inferno_device*) dev;
  106.     int nbits = bdev->nbits;
  107.     int mask = (1<<nbits)-1;
  108.  
  109.     /* make the colors the size we want */
  110.     if(gx_color_value_bits > nbits) {
  111.         shift = gx_color_value_bits - nbits;
  112.         red >>= shift;
  113.         green >>= shift;
  114.         blue >>= shift;
  115.     } else if(gx_color_value_bits < nbits) {
  116.         shift = nbits - gx_color_value_bits;
  117.         red <<= shift;
  118.         green <<= shift;
  119.         blue <<= shift;
  120.     }
  121.  
  122.     /* mask them off to be just nbits */
  123.     red   &= mask;
  124.     green &= mask;
  125.     blue  &= mask;
  126.  
  127.     /*
  128.      * we keep track of what ldepth bitmap this is by watching
  129.      * what colors gs asks for.
  130.      * 
  131.      * one catch: sometimes print_page gets called more than one
  132.      * per page (for multiple copies) without cmap calls inbetween.
  133.      * if bdev->cmapcall is 0 when print_page gets called, it uses
  134.      * the ldepth of the last page.
  135.      */
  136.     if(red == green && green == blue && red != 0 && red != mask) {
  137.         if(red == 5 || red == 10) {
  138.             if(bdev->ldepth < 1)
  139.                 bdev->ldepth = 1;
  140.         } else {
  141.             if(bdev->ldepth < 2)
  142.                 bdev->ldepth = 2;
  143.         }
  144.     } else
  145.         bdev->ldepth = 3;
  146.  
  147.     bdev->cmapcall = 1;
  148.     return ((((blue<<4)|green)<<4)|red);
  149. }
  150.  
  151. private int 
  152. inferno_cmap2rgb(P3(gx_device *dev, gx_color_index color,
  153.   gx_color_value rgb[3])) {
  154.     int shift, i;
  155.     inferno_device *bdev = (inferno_device*) dev;
  156.     int nbits = bdev->nbits;
  157.     int mask = (1<<nbits)-1;
  158.  
  159.     if(color < 0 || color > 255)
  160.         return_error(gs_error_rangecheck);
  161.  
  162.     rgb[2] = (color >> (2*nbits)) & mask;
  163.     rgb[1] = (color >> nbits) & mask;
  164.     rgb[0] = color & mask;
  165.     if(gx_color_value_bits > nbits) {
  166.         shift = gx_color_value_bits - nbits;
  167.         for(i=0; i<3; i++)
  168.             rgb[i] <<= shift;
  169.     } else if(gx_color_value_bits < nbits) {
  170.         shift = nbits - gx_color_value_bits;
  171.         for(i=0; i<3; i++)
  172.             rgb[i] >>= shift;
  173.     }
  174.  
  175.     return 0;
  176. }
  177.  
  178. /*
  179.  * dithering tables courtesy of john hobby
  180.  */
  181. /* The following constants and tables define the mapping from fine-grained RGB
  182.    triplets to 8-bit values based on the standard Plan 9 color map.
  183. */
  184. #define Rlevs    16        /* number of levels to cut red value into */
  185. #define Glevs    16
  186. #define Blevs    16
  187. #define Mlevs    16
  188. #define Rfactor 1        /* multiple of red level in p9color[] index */
  189. #define Gfactor Rlevs
  190. #define Bfactor    (Rlevs*Glevs)
  191.             
  192. ulong p9color[Rlevs*Glevs*Blevs];    /* index blue most sig, red least sig */
  193.  
  194. void init_p9color(void)        /* init at run time since p9color[] is so big */
  195. {
  196.     int r, g, b, o;
  197.     ulong* cur = p9color;
  198.     for (b=0; b<16; b++) {
  199.         for (g=0; g<16; g++) {
  200.         int m0 = (b>g) ? b : g;
  201.         for (r=0; r<16; r++) {
  202.             int V, M, rM, gM, bM, m;
  203.             int m1 = (r>m0) ? r : m0;
  204.             V=m1&3; M=(m1-V)<<1;
  205.             if (m1==0) m1=1;
  206.             m = m1 << 3;
  207.             rM=r*M; gM=g*M; bM=b*M;
  208.             *cur = 0;
  209.             for (o=7*m1; o>0; o-=2*m1) {
  210.             int rr=(rM+o)/m, gg=(gM+o)/m, bb=(bM+o)/m;
  211.             int ij = (rr<<6) + (V<<4) + ((V-rr+(gg<<2)+bb)&15);
  212.             *cur = (*cur << 8) + 255-ij;
  213.             }
  214.             cur++;
  215.         }
  216.         }
  217.     }
  218. }
  219.  
  220.  
  221. /*
  222.  * inferno_open() is supposed to initialize the device.
  223.  * there's not much to do.
  224.  */
  225. private int
  226. inferno_open(P1(gx_device *dev))
  227. {
  228.     inferno_device *bdev = (inferno_device*) dev;
  229.     setbuf(stderr, 0);
  230.     bdev->color = bdev->gray = 0;
  231.     bdev->cmapcall = 0;
  232.     bdev->ldepth = 3;
  233.     bdev->nbits = 4;    /* 4 bits per color per pixel (12 bpp, then we dither) */
  234.                 /* if you change this, change the entry in gs_inferno_device */
  235.     init_p9color();
  236.     return gdev_prn_open(dev);
  237. }
  238.  
  239. /* 
  240.  * inferno_close() is called at the end, once everything
  241.  * is finished.  we have nothing to do.
  242.  */
  243. private int
  244. inferno_close(P1(gx_device *dev))
  245. {
  246.     int code;
  247.     code = gdev_prn_close(dev);
  248.     if(code < 0)
  249.         return_error(code);
  250.     return 0;
  251. }
  252.  
  253. /*
  254.  * inferno_print_page() is called once for each page
  255.  * (actually once for each copy of each page, but we won't
  256.  * worry about that).
  257.  */
  258. private int
  259. inferno_print_page(P2(gx_device_printer *pdev, FILE *f))
  260. {
  261.     uchar buf[16384];    /* == 8192 dots across */
  262.     uchar *p;
  263.     WImage *w;
  264.     int bpl, y;
  265.     int x, xmod;
  266.     int ldepth;
  267.     int ppb[] = {8, 4, 2, 1};    /* pixels per byte */
  268.     int bpp[] = {1, 2, 4, 8};    /* bits per pixel */
  269.     int gsbpl;
  270.     ulong u;
  271.     ushort us;
  272.  
  273.     inferno_device *bdev = (inferno_device *) pdev;
  274.     Rectangle r;
  275.     setbuf(stderr, 0);
  276.  
  277.     gsbpl = gdev_prn_raster(pdev);
  278.     if(gsbpl > sizeof(buf)) {
  279.         fprintf(stderr, "bitmap far too wide for inferno\n");
  280.         return_error(gs_error_Fatal);
  281.     }
  282.  
  283.     if(bdev->cmapcall) {
  284.         bdev->lastldepth = bdev->ldepth;
  285.         bdev->ldepth = 0;
  286.         bdev->cmapcall = 0;
  287.     }
  288.     ldepth = bdev->lastldepth;
  289.  
  290.     r.min = ZP;
  291.     r.max.x = pdev->width;
  292.     r.max.y = pdev->height;
  293.     bpl = bytesperline(r, ldepth);
  294.     w = initwriteimage(f, r, ldepth);
  295.     if(w == nil) {
  296.         fprintf(stderr, "initwriteimage failed\n");
  297.         return_error(gs_error_Fatal);
  298.     }
  299.  
  300.     /*
  301.      * i wonder if it is faster to put the switch around the for loops
  302.      * to save all the ldepth lookups.
  303.      */
  304.     for(y=0; y<pdev->height; y++) {
  305.         gdev_prn_get_bits(pdev, y, buf, &p);
  306.         for(x=0; x<pdev->width; x++) {
  307.             us = (p[2*x]<<8) | p[2*x+1];
  308.             switch(ldepth) {
  309.             case 3:
  310.                 if(0){
  311.                     int r, g, b;
  312.                     r = us & 0xf;
  313.                     g = (us>>4)&0xf;
  314.                     b = (us>>8)&0xf;
  315.                     r<<=4;
  316.                     g<<=4;
  317.                     b<<=4;
  318.                     p[x] = rgb2cmap(r,g,b);    
  319.                 }
  320.                 if(1){
  321.                     u = p9color[us];
  322.                     /* the ulong in p9color is a 2x2 matrix.  pull the entry
  323.                      * u[x%2][y%2], more or less.
  324.                      */
  325.                     p[x] = u >> (8*((y%2)+2*(x%2)));
  326.                 }
  327.                 break;
  328.             case 2:
  329.                 us = ~us;
  330.                 if((x%2) == 0)
  331.                     p[x/2] = us & 0xf;
  332.                 else
  333.                     p[x/2] = (p[x/2]<<4)|(us&0xf);
  334.                 break;
  335.             case 0:
  336.                 us = ~us;
  337.                 if((x%8) == 0)
  338.                     p[x/8] = us & 0x1;
  339.                 else
  340.                     p[x/8] = (p[x/8]<<1)|(us&0x1);
  341.                 break;
  342.             }
  343.         }
  344.  
  345.         /* pad last byte over if we didn't fill it */
  346.         xmod = pdev->width % ppb[ldepth];
  347.         if(xmod)
  348.             p[(x-1)/ppb[ldepth]] <<= ((ppb[ldepth]-xmod)*bpp[ldepth]);
  349.         if(writeimageblock(w, p, bpl) == ERROR)
  350.             return_error(gs_error_Fatal);
  351.     }
  352.     if(writeimageblock(w, nil, 0) == ERROR)
  353.         return_error(gs_error_Fatal);
  354.  
  355.     return 0;
  356. }
  357.  
  358. /*
  359.  * this is a modified version of the image compressor
  360.  * from fb/bit2enc.  it is modified only in that it 
  361.  * now compiles as part of gs.
  362.  */
  363.  
  364. /*
  365.  * Compressed image file parameters
  366.  */
  367. #define    NMATCH    3        /* shortest match possible */
  368. #define    NRUN    (NMATCH+31)    /* longest match possible */
  369. #define    NMEM    1024        /* window size */
  370. #define    NDUMP    128        /* maximum length of dump */
  371. #define    NCBLOCK    6000        /* size of compressed blocks */
  372.  
  373. #define    HSHIFT    3    /* HSHIFT==5 runs slightly faster, but hash table is 64x bigger */
  374. #define    NHASH    (1<<(HSHIFT*NMATCH))
  375. #define    HMASK    (NHASH-1)
  376. #define    hupdate(h, c)    ((((h)<<HSHIFT)^(c))&HMASK)
  377.  
  378. typedef struct Dump    Dump;
  379. typedef struct Hlist Hlist;
  380.  
  381. struct Hlist{
  382.     ulong p;
  383.     Hlist *next, *prev;
  384. };
  385.  
  386. struct Dump {
  387.     int ndump;
  388.     uchar *dumpbuf;
  389.     uchar buf[1+NDUMP];
  390. };
  391.  
  392. struct WImage {
  393.     FILE *f;
  394.  
  395.     /* image attributes */
  396.     Rectangle origr, r;
  397.     int bpl;
  398.  
  399.     /* output buffer */
  400.     uchar outbuf[NCBLOCK], *outp, *eout, *loutp;
  401.  
  402.     /* sliding input window */
  403.     /*
  404.      * ibase is the pointer to where the beginning of
  405.      * the input "is" in memory.  whenever we "slide" the
  406.      * buffer N bytes, what we are actually doing is 
  407.      * decrementing ibase by N.
  408.      * the ulongs in the Hlist structures are just
  409.      * pointers relative to ibase.
  410.      */
  411.     uchar *inbuf;    /* inbuf should be at least NMEM+NRUN+NMATCH long */
  412.     uchar *ibase;
  413.     int minbuf;    /* size of inbuf (malloc'ed bytes) */
  414.     int ninbuf;    /* size of inbuf (filled bytes) */
  415.     ulong line;    /* the beginning of the line we are currently encoding,
  416.              * relative to inbuf (NOT relative to ibase) */
  417.  
  418.     /* raw dump buffer */
  419.     Dump dump;
  420.  
  421.     /* hash tables */
  422.     Hlist hash[NHASH];
  423.     Hlist chain[NMEM], *cp;
  424.     int h;
  425.     int needhash;
  426. };
  427.  
  428. private void
  429. zerohash(WImage *w)
  430. {
  431.     memset(w->hash, 0, sizeof(w->hash));
  432.     memset(w->chain, 0, sizeof(w->chain));
  433.     w->cp=w->chain;
  434.     w->needhash = 1;
  435. }
  436.  
  437. private int
  438. addbuf(WImage *w, uchar *buf, int nbuf)
  439. {
  440.     int n;
  441.     if(buf == nil || w->outp+nbuf > w->eout) {
  442.         if(w->loutp==w->outbuf){    /* can't really happen -- we checked line length above */
  443.             fprintf(stderr, "buffer too small for line\n");
  444.             return ERROR;
  445.         }
  446.         n=w->loutp-w->outbuf;
  447.         fprintf(w->f, "%11d %11d ", w->r.max.y, n);
  448.         fwrite(w->outbuf, 1, n, w->f);
  449.         w->r.min.y=w->r.max.y;
  450.         w->outp=w->outbuf;
  451.         w->loutp=w->outbuf;
  452.         zerohash(w);
  453.         return -1;
  454.     }
  455.  
  456.     memmove(w->outp, buf, nbuf);
  457.     w->outp += nbuf;
  458.     return nbuf;
  459. }
  460.  
  461. /* return 0 on success, -1 if buffer is full */
  462. private int
  463. flushdump(WImage *w)
  464. {
  465.     int n = w->dump.ndump;
  466.  
  467.     if(n == 0)
  468.         return 0;
  469.  
  470.     w->dump.buf[0] = 0x80|(n-1);
  471.     if((n=addbuf(w, w->dump.buf, n+1)) == ERROR)
  472.         return ERROR;
  473.     if(n < 0)
  474.         return -1;
  475.     w->dump.ndump = 0;
  476.     return 0;
  477. }
  478.  
  479. private void
  480. updatehash(WImage *w, uchar *p, uchar *ep)
  481. {
  482.     uchar *q;
  483.     Hlist *cp;
  484.     Hlist *hash;
  485.     int h;
  486.  
  487.     hash = w->hash;
  488.     cp = w->cp;
  489.     h = w->h;
  490.     for(q=p; q<ep; q++) {
  491.         if(cp->prev)
  492.             cp->prev->next = cp->next;
  493.         cp->next = hash[h].next;
  494.         cp->prev = &hash[h];
  495.         cp->prev->next = cp;
  496.         if(cp->next)
  497.             cp->next->prev = cp;
  498.         cp->p = q - w->ibase;
  499.         if(++cp == w->chain+NMEM)
  500.             cp = w->chain;
  501.         if(&q[NMATCH] < &w->inbuf[w->ninbuf])
  502.             h = hupdate(h, q[NMATCH]);
  503.     }
  504.     w->cp = cp;
  505.     w->h = h;
  506. }
  507.  
  508. /*
  509.  * attempt to process a line of input,
  510.  * returning the number of bytes actually processed.
  511.  *
  512.  * if the output buffer needs to be flushed, we flush
  513.  * the buffer and return 0.
  514.  * otherwise we return bpl
  515.  */
  516. private int
  517. gobbleline(WImage *w)
  518. {
  519.     int runlen, n, offs;
  520.     uchar *eline, *es, *best, *p, *s, *t;
  521.     Hlist *hp;
  522.     uchar buf[2];
  523.     int rv;
  524.  
  525.     if(w->needhash) {
  526.         w->h = 0;
  527.         for(n=0; n!=NMATCH; n++)
  528.             w->h = hupdate(w->h, w->inbuf[w->line+n]);
  529.         w->needhash = 0;
  530.     }
  531.     w->dump.ndump=0;
  532.     eline=w->inbuf+w->line+w->bpl;
  533.     for(p=w->inbuf+w->line;p!=eline;){
  534.         es = (eline < p+NRUN) ? eline : p+NRUN;
  535.  
  536.         best=nil;
  537.         runlen=0;
  538.         /* hash table lookup */
  539.         for(hp=w->hash[w->h].next;hp;hp=hp->next){
  540.             /*
  541.              * the next block is an optimization of 
  542.              * for(s=p, t=w->ibase+hp->p; s<es && *s == *t; s++, t++)
  543.              *     ;
  544.              */
  545.  
  546.             {    uchar *ss, *tt;
  547.                 s = p+runlen;
  548.                 t = w->ibase+hp->p+runlen;
  549.                 for(ss=s, tt=t; ss>=p && *ss == *tt; ss--, tt--)
  550.                     ;
  551.                 if(ss < p)
  552.                     while(s<es && *s == *t)
  553.                         s++, t++;
  554.             }
  555.  
  556.             n = s-p;
  557.  
  558.             if(n > runlen) {
  559.                 runlen = n;
  560.                 best = w->ibase+hp->p;
  561.                 if(p+runlen == es)
  562.                     break;
  563.             }
  564.         }
  565.  
  566.         /*
  567.          * if we didn't find a long enough run, append to 
  568.          * the raw dump buffer
  569.          */
  570.         if(runlen<NMATCH){
  571.             if(w->dump.ndump==NDUMP) {
  572.                 if((rv = flushdump(w)) == ERROR)
  573.                     return ERROR;
  574.                 if(rv < 0)
  575.                     return 0;
  576.             }
  577.             w->dump.dumpbuf[w->dump.ndump++]=*p;
  578.             runlen=1;
  579.         }else{
  580.         /*
  581.          * otherwise, assuming the dump buffer is empty,
  582.          * add the compressed rep.
  583.          */
  584.             if((rv = flushdump(w)) == ERROR)
  585.                 return ERROR;
  586.             if(rv < 0)
  587.                 return 0;
  588.             offs=p-best-1;
  589.             buf[0] = ((runlen-NMATCH)<<2)|(offs>>8);
  590.             buf[1] = offs&0xff;
  591.             if(addbuf(w, buf, 2) < 0)
  592.                 return 0;
  593.         }
  594.  
  595.         /*
  596.          * add to hash tables what we just encoded
  597.          */
  598.         updatehash(w, p, p+runlen);
  599.         p += runlen;
  600.     }
  601.  
  602.     if((rv = flushdump(w)) == ERROR)
  603.         return ERROR;
  604.     if(rv < 0)
  605.         return 0;
  606.     w->line += w->bpl;
  607.     w->loutp=w->outp;
  608.     w->r.max.y++;
  609.     return w->bpl;
  610. }
  611.  
  612. private uchar*
  613. shiftwindow(WImage *w, uchar *data, uchar *edata)
  614. {
  615.     int n, m;
  616.  
  617.     /* shift window over */
  618.     if(w->line > NMEM) {
  619.         n = w->line-NMEM;
  620.         memmove(w->inbuf, w->inbuf+n, w->ninbuf-n);
  621.         w->line -= n;
  622.         w->ibase -= n;
  623.         w->ninbuf -= n;
  624.     }
  625.  
  626.     /* fill right with data if available */
  627.     if(w->minbuf > w->ninbuf && edata > data) {
  628.         m = w->minbuf - w->ninbuf;
  629.         if(edata-data < m)
  630.             m = edata-data;
  631.         memmove(w->inbuf+w->ninbuf, data, m);
  632.         data += m;
  633.         w->ninbuf += m;
  634.     }
  635.  
  636.     return data;
  637. }
  638.  
  639. private WImage*
  640. initwriteimage(FILE *f, Rectangle r, int ldepth)
  641. {
  642.     WImage *w;
  643.     int n, bpl;
  644.  
  645.     bpl = bytesperline(r, ldepth);
  646.     if(r.max.y <= r.min.y || r.max.x <= r.min.x || bpl <= 0) {
  647.         fprintf(stderr, "bad rectangle, ldepth");
  648.         return nil;
  649.     }
  650.  
  651.     n = NMEM+NMATCH+NRUN+bpl*2;
  652.     w = malloc(n+sizeof(*w));
  653.     if(w == nil)
  654.         return nil;
  655.     w->inbuf = (uchar*) &w[1];
  656.     w->ibase = w->inbuf;
  657.     w->line = 0;
  658.     w->minbuf = n;
  659.     w->ninbuf = 0;
  660.     w->origr = r;
  661.     w->r = r;
  662.     w->r.max.y = w->r.min.y;
  663.     w->eout = w->outbuf+sizeof(w->outbuf);
  664.     w->outp = w->loutp = w->outbuf;
  665.     w->bpl = bpl;
  666.     w->f = f;
  667.     w->dump.dumpbuf = w->dump.buf+1;
  668.     w->dump.ndump = 0;
  669.     zerohash(w);
  670.  
  671.     fprintf(f, "compressed\n%11d %11d %11d %11d %11d ",
  672.         ldepth, r.min.x, r.min.y, r.max.x, r.max.y);
  673.     return w;
  674. }
  675.  
  676. private int
  677. writeimageblock(WImage *w, uchar *data, int ndata)
  678. {
  679.     uchar *edata;
  680.  
  681.     if(data == nil) {    /* end of data, flush everything */
  682.         while(w->line < w->ninbuf)
  683.             if(gobbleline(w) == ERROR)
  684.                 return ERROR;
  685.         addbuf(w, nil, 0);
  686.         if(w->r.min.y != w->origr.max.y) {
  687.             fprintf(stderr, "not enough data supplied to writeimage\n");
  688.         }
  689.         free(w);
  690.         return 0;
  691.     }
  692.  
  693.     edata = data+ndata;
  694.     data = shiftwindow(w, data, edata);
  695.     while(w->ninbuf >= w->line+w->bpl+NMATCH) {
  696.         if(gobbleline(w) == ERROR)
  697.             return ERROR;
  698.         data = shiftwindow(w, data, edata);
  699.     }
  700.     if(data != edata) {
  701.         fprintf(w->f, "data != edata.  uh oh\n");
  702.         return ERROR; /* can't happen */
  703.     }
  704.     return 0;
  705. }
  706.  
  707. /*
  708.  * functions from the Plan9/Brazil drawing libraries 
  709.  */
  710. private int
  711. bytesperline(Rectangle r, int ld)
  712. {
  713.     ulong ws, l, t;
  714.     int bits = 8;
  715.  
  716.     ws = bits>>ld;    /* pixels per unit */
  717.     if(r.min.x >= 0){
  718.         l = (r.max.x+ws-1)/ws;
  719.         l -= r.min.x/ws;
  720.     }else{            /* make positive before divide */
  721.         t = (-r.min.x)+ws-1;
  722.         t = (t/ws)*ws;
  723.         l = (t+r.max.x+ws-1)/ws;
  724.     }
  725.     return l;
  726. }
  727.  
  728. private int
  729. rgb2cmap(int cr, int cg, int cb)
  730. {
  731.     int r, g, b, v, cv;
  732.  
  733.     if(cr < 0)
  734.         cr = 0;
  735.     else if(cr > 255)
  736.         cr = 255;
  737.     if(cg < 0)
  738.         cg = 0;
  739.     else if(cg > 255)
  740.         cg = 255;
  741.     if(cb < 0)
  742.         cb = 0;
  743.     else if(cb > 255)
  744.         cb = 255;
  745.     r = cr>>6;
  746.     g = cg>>6;
  747.     b = cb>>6;
  748.     cv = cr;
  749.     if(cg > cv)
  750.         cv = cg;
  751.     if(cb > cv)
  752.         cv = cb;
  753.     v = (cv>>4)&3;
  754.     return 255-((((r<<2)+v)<<4)+(((g<<2)+b+v-r)&15));
  755. }
  756.  
  757. /*
  758.  * go the other way; not currently used.
  759.  *
  760. private long
  761. cmap2rgb(int c)
  762. {
  763.     int j, num, den, r, g, b, v, rgb;
  764.  
  765.     c = 255-c;
  766.     r = c>>6;
  767.     v = (c>>4)&3;
  768.     j = (c-v+r)&15;
  769.     g = j>>2;
  770.     b = j&3;
  771.     den=r;
  772.     if(g>den)
  773.         den=g;
  774.     if(b>den)
  775.         den=b;
  776.     if(den==0) {
  777.         v *= 17;
  778.         rgb = (v<<16)|(v<<8)|v;
  779.     }
  780.     else{
  781.         num=17*(4*den+v);
  782.         rgb = ((r*num/den)<<16)|((g*num/den)<<8)|(b*num/den);
  783.     }
  784.     return rgb;
  785. }
  786.  *
  787.  *
  788.  */
  789.  
  790.